package com.atguigu.shangrongbao.base.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.lettuce.LettuceConnection;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import javax.annotation.Resource;

/**
 * redis配置类
 * 当我们的数据存储到Redis的时候，我们的键（key）和值（value）都是通过Spring提供的Serializer序列化到数据库的。RedisTemplate默认使用的是JdkSerializationRedisSerializer，StringRedisTemplate默认使用的是StringRedisSerializer。
 *
 * Spring-data-redis的序列化类有下面这几个：GenericToStringSerializer: 可以将任何对象泛化为字符串并序列化；Jackson2JsonRedisSerializer: 跟JacksonJsonRedisSerializer实际上是一样的；JacksonJsonRedisSerializer: 序列化object对象为json字符串；JdkSerializationRedisSerializer: 序列化java对象（被序列化的对象必须实现Serializable接口）；StringRedisSerializer: 简单的字符串序列化；GenericToStringSerializer:类似StringRedisSerializer的字符串序列化；GenericJackson2JsonRedisSerializer:类似Jackson2JsonRedisSerializer，但使用时构造函数不用特定的类参考以上序列化,自定义序列化类;
 *
 * Spring Data JPA为我们提供了下面的Serializer：GenericToStringSerializer、Jackson2JsonRedisSerializer、JacksonJsonRedisSerializer、JdkSerializationRedisSerializer、OxmSerializer、StringRedisSerializer。
 *
 * JdkSerializationRedisSerializer: 使用JDK提供的序列化功能。优点是反序列化时不需要提供类型信息(class)，但缺点是需要实现Serializable接口，还有序列化后的结果非常庞大，是JSON格式的5倍左右，这样就会消耗redis服务器的大量内存。
 *
 * Jackson2JsonRedisSerializer： 使用Jackson库将对象序列化为JSON字符串。优点是速度快，序列化后的字符串短小精悍，不需要实现Serializable接口。但缺点也非常致命，那就是此类的构造函数中有一个类型参数，必须提供要序列化对象的类型信息(.class对象)。 通过查看源代码，发现其只在反序列化过程中用到了类型信息。
 *
 * key和hashKey：推荐使用     StringRedisSerializer: 简单的字符串序列化
 *
 * hashValue：推荐使用     GenericJackson2JsonRedisSerializer:类似Jackson2JsonRedisSerializer，但使用时构造函数不用特定的类
 *
 */
@Configuration
public class RedisConfig {

    /**
     * 为什么重写redis序列化
     *    因为RedisTemplate默认使用的是JdkSerializationRedisSerializer，会出现2个问题
     *      1. 被序列化的对象必须实现Serializable接口
     *      2. 被序列化被二进制化,导致值可读性差
     */

    //lettuceConnectionFactory  这个类必须使用@Resource 这个注入，使用参数注入会报错(** 不清楚什么原因 **)
    @Resource
    private LettuceConnectionFactory lettuceConnectionFactory;

    @Bean
    public RedisTemplate<String, Object> redisTemplate(){
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        //设置链接池
        redisTemplate.setConnectionFactory(lettuceConnectionFactory);

        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        // redisTemplate.setHashKeySerializer(stringRedisSerializer);
        //设置键key序列化
        redisTemplate.setKeySerializer(stringRedisSerializer);
        //设置值value序列化
        /**
         * 当设置了key和value的序列化后就可以实现key和value的json化
         *    但是日期格式无法正确转换
         *    这是字典数据格式化后的  日期对象直接格式化成了一个map
         *    {"id":1,"parentId":0,"name":"全部分类","value":null,"dictCode":"ROOT","createTime":{"month":"AUGUST","dayOfWeek":"TUESDAY","dayOfYear":229,"nano":0,"year":2021,"monthValue":8,"dayOfMonth":17,"hour":3,"minute":33,"second":7,"chronology":{"id":"ISO","calendarType":"iso8601"}},"updateTime":{"month":"AUGUST","dayOfWeek":"TUESDAY","dayOfYear":229,"nano":0,"year":2021,"monthValue":8,"dayOfMonth":17,"hour":3,"minute":33,"second":7,"chronology":{"id":"ISO","calendarType":"iso8601"}},"deleted":false}
         */
        Jackson2JsonRedisSerializer<Object> objectJackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        redisTemplate.setValueSerializer(objectJackson2JsonRedisSerializer);
        /**
         * 设置日期序列化
         *   这样可以json格式存在一个字符串的日期格式
         *   {"id":1,"parentId":0,"name":"全部分类","value":null,"dictCode":"ROOT","createTime":"2021-08-17T03:33:07","updateTime":"2021-08-17T03:33:07","deleted":false}
         *   要序列化数据一定是
         */
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        objectMapper.registerModule(new JavaTimeModule());

        /**
         * 序列化成功，但是反序列化却不成功，当反序列化的时候无法知道具体的哪个类，无法装配
         */
        //将当前对象的数据类型也存入序列化的结果字符串中
        objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);

        objectJackson2JsonRedisSerializer.setObjectMapper(objectMapper);


        return redisTemplate;

    }
}
